#!/usr/bin/env ruby

# Assumptions:
#
# - It's okay to ignore mephisto's sections and sites.
# - Each mephisto tag should be converted to a Nesta category.

require "getoptlong"
require "time"

require "rubygems"
require "active_record"

require File.join(File.dirname(__FILE__), *%w[.. lib config])
require File.join(File.dirname(__FILE__), *%w[.. lib models])

class Content < ActiveRecord::Base
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
  
  def self.inheritance_column
    @inheritance_column = "none"
  end
  
  def self.articles
    conditions = "type = 'Article' and published_at is not NULL"
    find(:all, :conditions => conditions).map { |c| ArticleLoader.new(c) }
  end
end

class Tagging < ActiveRecord::Base
  belongs_to :content
  belongs_to :tag
end

class Tag < ActiveRecord::Base
  has_many :taggings
  has_many :contents, :through => :taggings

  def filename
    File.join(Nesta::Config.page_path, "#{name}.mdown")
  end
end

module ContentWrapper
  def initialize(content)
    @content = content
  end
  
  def permalink
    @content.permalink
  end
  
  private
    def metadata_string(metadata)
      metadata.map { |key, value| "#{key}: #{value}" }.sort.join("\n")
    end
end  

class ArticleLoader
  include ContentWrapper
  
  def filename
    File.join(Nesta::Config.page_path, "#{permalink}.mdown")
  end

  def date
    @content.published_at
  end
  
  def categories
    @content.tags.map { |t| t.name }.sort.join(", ")
  end
  
  def summary
    @content.excerpt.strip.gsub(/\r?\n/, '\n')
  end
  
  def atom_id(domain)
    published = "#{@content.created_at.strftime('%Y-%m-%d')}"
    "tag:#{domain},#{published}:#{@content.id}"
  end

  def metadata(domain)
    metadata = {
      "Date" => date,
      "Categories" => categories,
      "Summary" => summary
    }
    metadata.merge!("Atom ID" => atom_id(domain)) if domain
    metadata_string(metadata)
  end
  
  def content
    ["# #{@content.title}", @content.body.gsub("\r\n", "\n")].join("\n\n")
  end
end

class App
  def usage
    $stderr.write <<-EOF
Usage: #{File.basename $0} [OPTIONS] -u <username> -p <password>

OPTIONS (defaults shown in brackets)

    -a, --adapter     Database adapter (mysql)
    --domain          Site's domain name (for preserving <id/> tags in feed)
    --clobber         Overwrite existing files
    -d, --database    Database name (mephisto_production)
    -h, --host        Database hostname (localhost)
    -p, --password    Database password
    -u, --username    Database username

    EOF
    exit 1
  end
  
  def parse_command_line
    parser = GetoptLong.new
    parser.set_options(
        ["-a", "--adapter", GetoptLong::REQUIRED_ARGUMENT],
        ["--domain", GetoptLong::REQUIRED_ARGUMENT],
        ["--clobber", GetoptLong::NO_ARGUMENT],
        ["-d", "--database", GetoptLong::REQUIRED_ARGUMENT],
        ["-h", "--host", GetoptLong::REQUIRED_ARGUMENT],
        ["-u", "--username", GetoptLong::REQUIRED_ARGUMENT],
        ["-p", "--password", GetoptLong::REQUIRED_ARGUMENT])
    loop do
      opt, arg = parser.get
      break if not opt
      case opt
        when "-a"
          @adapter = arg
        when "--domain"
          @domain = arg
        when "--clobber"
          @clobber = true
        when "-d"
          @database = arg
        when "-h"
          @host = arg
        when "-p"
          @password = arg
        when "-u"
          @username = arg
      end
    end
    @adapter ||= "mysql"
    @clobber.nil? && @clobber = false
    @host ||= "localhost"
    @database ||= "mephisto_production"
    usage if @username.nil? || @password.nil?
  end
  
  def connect_to_database
    ActiveRecord::Base.establish_connection( 
      :adapter => @adapter,
      :host => @host,
      :username => @username,
      :password => @password,
      :database => @database
    ) 
  end
  
  def should_import?(item)
    if File.exist?(item.filename) && (! @clobber)
      puts "skipping (specify --clobber to overwrite)"
      false
    else
      true
    end
  end
  
  def import_articles
    Content.articles.each do |article|
      puts "Importing article: #{article.permalink}"
      if should_import?(article)
        File.open(article.filename, "w") do |file|
          file.write [article.metadata(@domain), article.content].join("\n\n")
        end
      end
    end
  end
  
  def import_tags
    Tag.find(:all).each do |tag|
      puts "Importing tag: #{tag.name}"
      if should_import?(tag)
        File.open(tag.filename, "w") do |file|
          file.write("# #{tag.name.capitalize}\n")
        end
      end
    end
  end
  
  def main
    parse_command_line
    connect_to_database
    import_articles
    import_tags
  rescue GetoptLong::Error
    exit 1
  end
end

app = App.new
app.main
